mixin themeCss
	link(id="dark-v1-theme-link-tag", rel=(userSettings.uiTheme == "dark-v1" ? "stylesheet" : false), href=assetUrl(`./style/dark-v1.min.css`), integrity=assetIntegrity("dark-v1.min.css"), crossorigin="anonymous")
	link(id="light-theme-link-tag", rel=(userSettings.uiTheme == "light" ? "stylesheet" : false), href=assetUrl(`./style/light.min.css`), integrity=assetIntegrity("light.min.css"), crossorigin="anonymous")
	link(id="dark-theme-link-tag", rel=(userSettings.uiTheme == "dark" ? "stylesheet" : false), href=assetUrl(`./style/dark.min.css`), integrity=assetIntegrity("dark.min.css"), crossorigin="anonymous")

	style.
		@font-face {
			font-family: "bootstrap-icons";

			src:	url("!{assetUrl("./font/bootstrap-icons.woff2")}") format("woff2"),
					url("!{assetUrl("./font/bootstrap-icons.woff")}") format("woff");
		}

	link(rel="stylesheet", href=assetUrl(`./style/bootstrap-icons.css`), integrity=assetIntegrity("bootstrap-icons.css"), crossorigin="anonymous")


mixin sharedScriptTags
	script(src=assetUrl(`./js/jquery.min.js`), integrity=assetIntegrity("jquery.min.js"), crossorigin="anonymous")
	script(src=assetUrl(`./js/bootstrap.bundle.min.js`), integrity=assetIntegrity("bootstrap.bundle.min.js"), crossorigin="anonymous")
	
	script(src=assetUrl(`./js/site.js`), integrity=assetIntegrity("site.js"), crossorigin="anonymous")


mixin pageTitle(text, subtext, copyableSubtext=false)
	.d-flex.justify-content-between
		h1.fw-light.word-wrap.mb-1
			| #{text}
		
		if (block)
			.text-right
				block

	h4.fw-light.word-wrap.mb-4 #{subtext}
		if (copyableSubtext)
			small
				+copyTextButton(subtext)
	
	//hr.mb-3


mixin sectionTitleBlock
	h3.h5.mb-1.fw-light
		block

			
mixin sectionTitle(text, toggle=false, toggleUniqueClass, toggleUserSettingName, toggleOpen, tooltipText)
	if (false)
		pre
			code.json #{JSON.stringify(session.userSettings)}

	h3.h5.mb-1.fw-light(class=(toggle && !toggleOpen ? "mb-section" : false), class=(!toggle || toggleOpen ? "d-block" : "d-none"))
		if (tooltipText)
			span.border-dotted(title=tooltipText, data-bs-toggle="tooltip")
				| #{text}

		else
			| #{text}

		if (toggle)
			small(title=`Toggle ${text}`, data-bs-toggle="tooltip")
				a.text-card-highlight.fs-6(href=`./changeSetting?name=${toggleUserSettingName}&value=false`)
					i.toggle-plus-minus.ms-2(class=(toggleOpen ? "bi-dash-square" : "bi-plus-square"))


mixin contentSection(title, toggleable=false, toggleUserSettingName, defaultOpen=true, cardUi=true)
	if (toggleable)
		- var toggleUniqueClass = `section-${utils.getRandomString(10, "aA#")}`;
		- var toggleOpen = userSettings[toggleUserSettingName] == null ? defaultOpen : (userSettings[toggleUserSettingName] == "true" || userSettings[toggleUserSettingName] == true);

	if (title)
		+sectionTitle(title, toggleable, toggleUniqueClass, toggleUserSettingName, toggleOpen)

	.mb-section(class=toggleUniqueClass, style=(toggleable && !toggleOpen ? "display: none;" : false))
		if (cardUi)
			.card.mb-section.shadow-sm.border-2
				.card-body
					block

		else
			block


mixin summaryRow(itemCount)
	- locals.summaryItemCount = itemCount;
	- locals.summaryItemIndex = 0;
	
	- locals.colCounts = {"sm": 1, "md": Math.min(itemCount, 3), "lg": Math.min(itemCount, 4), "xl": Math.min(itemCount, 5)};
	- locals.rowCountsArray = utils.objectProperties(locals.colCounts).map(x => [x, locals.colCounts[x]]).map(x => [x[0], (Math.floor(itemCount / x[1]) + ((itemCount % x[1] > 0) ? 1 : 0))]);
	- locals.rowCounts = {};
	- locals.rowCountsArray.forEach(x => locals.rowCounts[x[0]] = x[1]);

	//h1 #{itemCount} #{JSON.stringify(locals.rowCounts)}

	.row.row-cols-1.summary-row(class=utils.objectProperties(locals.colCounts).map(x => `row-cols-${x}-${locals.colCounts[x]}`).join(" "))
		block


mixin summaryTitle(title, titleDesc, subtitle, subtitleDesc, linkText, linkUrl, linkDesc)
	span.fs-6.text-uppercase.fw-light.text-card-highlight(class=(titleDesc ? "border-dotted" : false), class=(subtitle ? "me-2" : false), title=titleDesc, data-bs-toggle="tooltip", data-bs-html="true") #{title}
	if (subtitle)
		small.text-card-highlight.fw-light
			| (
			span(class=(subtitleDesc ? "border-dotted" : false), title=subtitleDesc, data-bs-toggle="tooltip", data-bs-html="true") #{subtitle}
			| )

	if (linkText && linkUrl)
		if (linkText.startsWith("text:"))
			a.ms-2.text-tiny(href=linkUrl, data-bs-toggle="tooltip", title=linkDesc) #{linkText.substring("text:".length)}

		else if (linkText.startsWith("icon:"))
			small
				a.ms-2(href=linkUrl, data-bs-toggle="tooltip", title=linkDesc)
					i(class=linkText.substring("icon:".length))


mixin summaryItem(title, titleDesc, subtitle, subtitleDesc, linkText, linkUrl, linkDesc)
	- var rowIndexes = utils.objectProperties(locals.colCounts).map(x => [x, locals.colCounts[x]]).map(x => [x[0], Math.floor(locals.summaryItemIndex / x[1])]);

	.col(class=(locals.summaryItemIndex == (locals.summaryItemCount - 1) ? "mb-0" : "mb-3"), class=rowIndexes.map(x => `mb-${x[0]}-${(x[1] < (locals.rowCounts[x[0]] - 1) ? "4" : "0")}`))
		//span.text-danger (#{JSON.stringify(locals.rowCounts)})
		.text-start.text-md-center
			+summaryTitle(title, titleDesc, subtitle, subtitleDesc, linkText, linkUrl, linkDesc)
			

		.lead.text-start.text-md-center
			block

	- locals.summaryItemIndex++;


// options keys:
// 		- blockHeightsByTxid
// 		- sharedBlockHeight (int)
// 		- showDates (true|false)
// 		- highlightAddress (address)
// 		- addrGainsByTxid (map)
// 		- addrLossesByTxid (map)
// 		- mempoolDetailsByTxid (map)

// txBlockHeightsByTxid_orSharedBlockHeight, showDates=false, highlightAddress, addrGainsByTxid, addrLossesByTxid)
mixin txList(visibleTxs, txCount, limit, offset, inputsByTxid, options={})
	if (txCount > limit && !options.hidePagination)
		.rounded-0.rounded-top.bg-content.border
			.p-3
				h6.fw-normal.mb-2 #{offset.toLocaleString()} - #{Math.min(offset + limit - 1, txCount - 1).toLocaleString()} of #{txCount.toLocaleString()}
				+pagination(limit, offset, null, txCount, paginationBaseUrl)
					
	
		//.mb-4.mb-md-3
		.pb-4.pb-md-2.bg-tx-separator


	each tx, txIndex in visibleTxs
		- var txBlockHeight = null;
		if (options.blockHeightsByTxid)
			- txBlockHeight = options.blockHeightsByTxid[tx.txid];
		else if (options.sharedBlockHeight != undefined)
			- txBlockHeight = options.sharedBlockHeight;

		- var tx_height_param = (!global.txindexAvailable && txBlockHeight) ? `@${txBlockHeight}` : '';

		- var txInputs = inputsByTxid[tx.txid];
		- var blockHeight = txBlockHeight != undefined ? txBlockHeight : -1;
		- var totalIOValues = utils.getTxTotalInputOutputValues(tx, txInputs, blockHeight);


		.border.bg-content.p-3(class=(txIndex == 0 && txCount <= limit ? "rounded-top" : false), class=(txCount <= limit && txIndex == (visibleTxs.length - 1) ? "rounded-bottom" : false))
			if (tx && tx.txid)
				.clearfix
					.float-start
						.mb-3.word-wrap
							span.badge.bg-primary.fw-normal.me-2(title=`#${(txIndex + offset).toLocaleString()}`, data-bs-toggle="tooltip")
								span.d-inline.d-sm-none.me-1 tx
								| ##{(txIndex + offset).toLocaleString()}
							
							a.d-inline.d-md-none.small(href=`./tx/${tx.txid}${tx_height_param}`) #{utils.ellipsizeMiddle(tx.txid, 24)}
							a.d-none.d-md-inline.d-lg-none(href=`./tx/${tx.txid}${tx_height_param}`) #{utils.ellipsizeMiddle(tx.txid, 24)}
							a.d-none.d-lg-inline(href=`./tx/${tx.txid}${tx_height_param}`) #{utils.ellipsizeMiddle(tx.txid, 48)}

							if (global.specialTransactions && global.specialTransactions[tx.txid])
								a.ms-2(data-bs-toggle="tooltip", title=`${coinConfig.name} Fun! There's something special about this transaction…`)
									+funItemIcon



					- var hasInputAddresses = false;
					- var coinbaseTx = false;
					if (tx && tx.vin && inputsByTxid && inputsByTxid[tx.txid])
						- var txInputs = inputsByTxid[tx.txid];
						each txVin, txVinIndex in tx.vin
							if (!txVin.coinbase)
								- var vout = null;
								if (txInputs[txVinIndex])
									- var voutAddresses = utils.getVoutAddresses(txInputs[txVinIndex]);

									if (voutAddresses.length > 0)
										- hasInputAddresses = true;

							else
								- coinbaseTx = true;

					if (hasInputAddresses)
						.float-end
							.d-block.d-sm-none
								a(href="javascript:void(0)", onclick=`$(this).find(".toggle-icon").toggleClass("bi-plus-square").toggleClass("bi-dash-square"); $(".tx-input-address-${tx.txid}").toggle();`)
									i.toggle-icon.bi-plus-square.text-card-highlight.text-end


					.float-start.float-sm-end.mt-n2.mt-sm-0.mb-2.mb-sm-0
						.me-5.me-sm-n2
							- let txFee = null;
							- let txFeeRate = null;
							
							if (options.mempoolDetailsByTxid && options.mempoolDetailsByTxid[tx.txid])
								- var mempoolDetails = options.mempoolDetailsByTxid[tx.txid].entry;
								- var mempoolDetailsFee = ((mempoolDetails.fees && mempoolDetails.fees.modified) ? mempoolDetails.fees.modified : (mempoolDetails.fee ? mempoolDetails.fee : 0));
								
								- txFee = new Decimal(mempoolDetailsFee);
								- txFeeRate = txFee.times(coinConfig.baseCurrencyUnit.multiplier).dividedBy(mempoolDetails.vsize);


							if (!coinbaseTx && totalIOValues.input && totalIOValues.output)
								- txFee = new Decimal(totalIOValues.input).minus(totalIOValues.output);
								- txFeeRate = txFee.dividedBy(tx.vsize).times(coinConfig.baseCurrencyUnit.multiplier);

							
							if (!coinbaseTx && Object.keys(txInputs).length < tx.vin.length)
								+infoBadge
									span(data-bs-toggle="tooltip", title="Data truncated by performance settings. See transaction details for fee info.")
										span.fw-light …

							else if (txFee)
								+infoBadge
									span.border-dotted.fw-bold.me-1(title="The miner fee (and fee rate) paid by this transaction. The fee equals the difference between the total input value and the total output value.", data-bs-toggle="tooltip") fee
									+_valueDisplaySat(txFee, 1000)

									if (txFee > 0)
										span.ms-2 (
											| #{parseInt(txFee.dividedBy(tx.vsize).times(coinConfig.baseCurrencyUnit.multiplier)).toLocaleString()}
											span.text-tiny.ms-1 sat/vB
											span )
								

							- let addrGain = null;
							- let addrLoss = null;

							if (options.addrGainsByTxid && options.addrGainsByTxid[tx.txid])
								- addrGain = new Decimal(options.addrGainsByTxid[tx.txid]);
								
								if (false)
									+successBadge
										span +
										+valueDisplay(options.addrGainsByTxid[tx.txid])

							if (options.addrLossesByTxid && options.addrLossesByTxid[tx.txid])
								- addrLoss = new Decimal(options.addrLossesByTxid[tx.txid]);
								
								if (false)
									span.badge.bg-danger.me-2
										span -
										+valueDisplay(options.addrLossesByTxid[tx.txid])

							- let addrDelta = null;

							if (addrGain && addrLoss)
								- addrDelta = addrGain.minus(addrLoss);

							else if (addrGain)
								- addrDelta = addrGain;

							else if (addrLoss)
								- addrDelta = addrLoss.times(-1);

							if (addrDelta)
								+darkBadge
									span.border-dotted.fw-bold.me-1(title="Net change to the balance of the current address caused by this transaction", data-bs-toggle="tooltip") net

									if (addrDelta > 0)
										span.text-success
											span +
											+valueDisplay(addrDelta)
									else
										span.text-danger
											span -
											+valueDisplay(addrDelta.times(-1))

							if (options.addrGainsByTxid && options.addrLossesByTxid && !options.addrGainsByTxid[tx.txid] && !options.addrLossesByTxid[tx.txid])
								span.badge.bg-danger.me-2(data-bs-toggle="tooltip", title="See transaction details for spent-from-this-address amount")
									span.fw-light …

							if (options.showConfirmations)
								+darkBadge
									if (txBlockHeight > 0 || tx.txid == '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b')
										span(title=`${(options.currentBlockHeight - txBlockHeight + 1).toLocaleString()} confirmation${(options.currentBlockHeight - txBlockHeight > 1) ? "s" :""}`, data-bs-toggle="tooltip")
											i.bi-lock.me-1
											- var confCount = options.currentBlockHeight - txBlockHeight + 1;
											- var confData = utils.formatLargeNumber(confCount);
											
											span.fw-light
												if (confCount < 1000)
													| #{confCount}
												else
													| #{parseInt(confData[0])}#{confData[1].abbreviation}

									else
										span(title=`Unconfirmed`, data-bs-toggle="tooltip")
											i.bi-unlock.text-danger.me-1
											span.fw-light.text-danger 0

							if (options.showDates && tx.time)
								- var utcMoment = moment.utc(new Date(parseInt(tx.time) * 1000));
								- var titleStr = `${utcMoment.format("Y-MM-DD HH:mm:ss")} utc`;

								.d-none.d-md-inline
									+darkBadge
										i.bi-clock.me-1

										span.fw-light
											+timeAgo(tx.time, {oneElement:true})

			
			hr.mb-4.mx-n3

			.d-none.d-lg-block.border-end.mt-n4.mb-2(style="border-color: currentColor !important; height: 10px; width: 50%; opacity: 0.25;")

			div
				+txIoDetails(tx, txInputs, totalIOValues, blockHeight, {highlightAddress:options.highlightAddress, maxTxOutputDisplayCount:maxTxOutputDisplayCount})

		if (txIndex < (visibleTxs.length - 1) || txCount > limit)
			.pb-4.pb-md-2.bg-tx-separator


	if (txCount > limit && !options.hidePagination)
		.rounded-0.rounded-bottom.bg-content.border
			.p-3
				if (!crawlerBot && txCount > limit)
					h6.fw-normal.mb-2 #{offset.toLocaleString()} - #{Math.min(offset + limit - 1, txCount - 1).toLocaleString()} of #{txCount.toLocaleString()}
					+pagination(limit, offset, null, txCount, paginationBaseUrl)


mixin pageTabs(names)
	ul.nav.nav-tabs#page-tabs
		each name, nameIndex in names
			li.nav-item.page-tab(style=(nameIndex == 0 ? "margin-left: 10px;" : false))
				a.nav-link(class=(nameIndex == 0 ? "active" : false), data-bs-toggle="tab", data-default-tab=(nameIndex == 0 ? "true" : false) href=`#${name.replace(" ", "-")}`, role="tab") #{name}

	.bg-gradient-body-to-main.pb-4.mb-2


mixin pillTabs(names)
	ul.nav.nav-pills.mb-3
		each name, nameIndex in names
			li.nav-item
				a.nav-link(class=(nameIndex == 0 ? "active" : false), data-bs-toggle="tab", href=`#${name.replace(" ", "-")}`, role="tab") #{name}


mixin pageTab(name, defaultActive=false)
	div.tab-pane(id=`${name.replace(" ", "-")}`, class=(defaultActive ? "active" : false), role="tabpanel")
		block


mixin valueDisplay(val, options={})
	if (val > 0)
		if (userSettings.displayCurrency == "btc")
			+_valueDisplayBtc(val, options)

		else if (userSettings.displayCurrency == "sat")
			+_valueDisplaySat(val, 1000000)

		else if (userSettings.displayCurrency == "local")
			+_valueDisplayLocal(val, 1000000)
				
	else
		span 0


mixin _valueDisplayBtc(val, options={})
	- var parts = utils.formatCurrencyAmount(val, userSettings.displayCurrency);

	//span #{JSON.stringify(parts)}

	//span.d-inline.d-sm-none.fw-light.me-tiny ₿
	
	span #{parts.val}
		if (parts.lessSignificantDigits && !options.hideLessSignificantDigits)
			span.text-small.text-darken.ms-1 #{parts.lessSignificantDigits}

	if (exchangeRates)
		- var localCurFormatData = utils.getExchangedCurrencyFormatData(val, userSettings.localCurrency);

		span.xs-hidden.d-none.d-sm-inline.text-tiny.text-tight-spacing.border-dotted.fw-light.ms-1(data-bs-toggle="tooltip", title=`<span class="me-tiny">${localCurFormatData.symbol}</span><span>${localCurFormatData.value}</span>`, data-bs-html="true") #{parts.currencyUnit}
			
	else
		span.xs-hidden.d-none.d-sm-inline.text-tiny.text-tight-spacing.fw-light.ms-1 #{parts.currencyUnit}


mixin _valueDisplaySat(val, summarizeMin=Infinity, summarizeDecimals=3)
	- var localCurrencyDisplay = userSettings.displayCurrency == "local";
	- var parts = utils.formatCurrencyAmount(val, "sat");

	//span #{JSON.stringify(parts)}
	
	if (Math.abs(parts.intVal) >= summarizeMin)
		//span #{JSON.stringify(parts)}
		- var largeNumberData = utils.formatLargeNumberSignificant(parts.intVal, summarizeDecimals >= 0 ? summarizeDecimals : 3);
		//span #{JSON.stringify(largeNumberData)} #{parts.intVal}
		span(title=parts.intVal.toLocaleString(), data-bs-toggle="tooltip") #{largeNumberData[0]}
		span #{largeNumberData[1].abbreviation}
		
	else
		span(class=(localCurrencyDisplay ? "border-dotted" : false), title=(localCurrencyDisplay ? `${utils.formatCurrencyAmount(val, coinConfig.defaultCurrencyUnit.name.toLowerCase()).simpleVal} BTC` : false), data-bs-toggle=(localCurrencyDisplay ? "tooltip" : false)) #{parts.intVal.toLocaleString()}


	if (exchangeRates)
		- var localCurFormatData = utils.getExchangedCurrencyFormatData(val, userSettings.localCurrency);

		span.xs-hidden.d-none.d-sm-inline.text-tiny.text-tight-spacing.border-dotted.fw-light.ms-1(data-bs-toggle="tooltip", title=`<span class="me-tiny">${localCurFormatData.symbol}</span><span>${localCurFormatData.value}</span>`, data-bs-html="true") #{parts.currencyUnit}
			
	else
		span.xs-hidden.d-none.d-sm-inline.text-tiny.text-tight-spacing.fw-light.ms-1 #{parts.currencyUnit}


mixin _valueDisplayLocal(val, summarizeMin=Infinity, summarizeDecimals=3)
	- var localCurrencyDisplay = userSettings.displayCurrency == "local";

	- var parts = utils.formatCurrencyAmount(val, userSettings.localCurrency);

	//span #{JSON.stringify(parts)}
	span.fw-light.me-tiny #{global.currencySymbols[userSettings.localCurrency]}

	if (Math.abs(parts.intVal) >= summarizeMin)
		- var largeNumberData = utils.formatLargeNumberSignificant(parts.intVal, summarizeDecimals >= 0 ? summarizeDecimals : 3);
		//span #{JSON.stringify(largeNumberData)} #{parts.intVal}
		span.border-dotted(title=`${global.currencySymbols[userSettings.localCurrency]}${parts.intVal.toLocaleString()}<br/>${utils.formatCurrencyAmount(val, coinConfig.defaultCurrencyUnit.name.toLowerCase()).simpleVal} BTC`, data-bs-toggle="tooltip", data-bs-html="true")
			span #{largeNumberData[0]}
			span.ms-1 #{largeNumberData[1].textDesc}

	else
		span.border-dotted(title=`${utils.formatCurrencyAmount(val, coinConfig.defaultCurrencyUnit.name.toLowerCase()).simpleVal} BTC`, data-bs-toggle="tooltip") #{parts.val}
	

mixin valueDisplaySpecial(val, maxDecimals=-1, summarizeAlways=false, summarizeMin=Infinity, summarizeDecimals=-1)
	if (maxDecimals == -1 && summarizeAlways == false && summarizeMin == undefined)
		+valueDisplay(val)

	else
		if (val > 0)
			- var localCurrencyDisplay = userSettings.displayCurrency == "local";

			if (maxDecimals > 0)
				- var parts = utils.formatCurrencyAmountWithForcedDecimalPlaces(val, localCurrencyDisplay ? userSettings.localCurrency : userSettings.displayCurrency, maxDecimals);

			else
				- var parts = utils.formatCurrencyAmount(val, localCurrencyDisplay ? userSettings.localCurrency : userSettings.displayCurrency);

			//span #{JSON.stringify(parts)}
			if (localCurrencyDisplay)
				span.me-1 #{global.currencySymbols[userSettings.localCurrency]}
				//| #{JSON.stringify(parts)}

			if (summarizeAlways || parts.intVal >= summarizeMin)
				//span #{JSON.stringify(parts)}
				- var largeNumberData = utils.formatLargeNumberSignificant(parts.intVal, summarizeDecimals >= 0 ? summarizeDecimals : 2);
				//span #{JSON.stringify(largeNumberData)} #{parts.intVal}
				span(class=(localCurrencyDisplay ? "border-dotted" : false), title=(localCurrencyDisplay ? `${utils.formatCurrencyAmount(val, coinConfig.defaultCurrencyUnit.name.toLowerCase()).simpleVal} BTC` : false), data-bs-toggle=(localCurrencyDisplay ? "tooltip" : false)) #{largeNumberData[0]} #{largeNumberData[1].textDesc}
			
			else if (maxDecimals == 0)
				span(class=(localCurrencyDisplay ? "border-dotted" : false), title=(localCurrencyDisplay ? `${utils.formatCurrencyAmount(val, coinConfig.defaultCurrencyUnit.name.toLowerCase()).simpleVal} BTC` : false), data-bs-toggle=(localCurrencyDisplay ? "tooltip" : false)) #{parts.intVal.toLocaleString()}

			else if (maxDecimals > 0)
				span(class=(localCurrencyDisplay ? "border-dotted" : false), title=(localCurrencyDisplay ? `${utils.formatCurrencyAmount(val, coinConfig.defaultCurrencyUnit.name.toLowerCase()).simpleVal} BTC` : false), data-bs-toggle=(localCurrencyDisplay ? "tooltip" : false)) #{parts.val}
			
			else
				span(class=(localCurrencyDisplay ? "border-dotted" : false), title=(localCurrencyDisplay ? `${utils.formatCurrencyAmount(val, coinConfig.defaultCurrencyUnit.name.toLowerCase()).simpleVal} BTC` : false), data-bs-toggle=(localCurrencyDisplay ? "tooltip" : false)) #{parts.val}
					if (parts.lessSignificantDigits)
						span.text-small.text-muted.ms-1 #{parts.lessSignificantDigits}

			if (userSettings.displayCurrency != "local")
				if (exchangeRates)
					- var localCurFormatData = utils.getExchangedCurrencyFormatData(val, userSettings.localCurrency);

					span.text-tiny.text-tight-spacing.border-dotted.fw-light.ms-1(data-bs-toggle="tooltip", title=`<span class="me-tiny">${localCurFormatData.symbol}</span><span>${localCurFormatData.value}</span>`, data-bs-html="true") #{parts.currencyUnit}
						
				else
					span.text-tiny.text-tight-spacing.fw-light.ms-1 #{parts.currencyUnit}
						
			else if (userSettings.displayCurrency == "local")
					
		else
			span 0


mixin confirmations(confirmations, abbreviated=true)
	if (confirmations < 6)
		span.text-warning.border-dotted(title="Fewer than 6 confirmations is generally considered <b>unsettled</b> for high-value transactions because block <b>re-organizations</b> that could affect them have reasonable probability.", data-bs-toggle="tooltip", data-bs-html="true") #{confirmations.toLocaleString()}
			i.bi-unlock.ms-1
	else
		span.border-dotted.text-success(title="6 confirmations is generally considered <b>settled</b>. High-value transactions may require more;<br/>Low-value transactions may require fewer.", data-bs-toggle="tooltip", data-bs-html="true")
			| #{abbreviated && confirmations > 1000 ? `${Math.floor(confirmations/1000)}k` : confirmations.toLocaleString()}
			i.bi-check2.ms-1



mixin bodyBgBadge(text)
	span.small.badge.bg-body.border.border-card-highlight-badge.text-reset.mb-tiny.me-1 #{text ? text : ""}
		if (block)
			block

mixin cardHighlightBadge(text)
	span.small.badge.card-highlight.border.border-card-highlight-badge.text-reset.mb-tiny.me-1 #{text ? text : ""}
		if (block)
			block



mixin hexDataDisplay(hex, order="utf8,ascii,hex")
	- var items = order.split(",");
	- var uniqueId = utils.getRandomString(12, "aA");

	.d-flex.flex-row
		.d-inline-block.small.border.rounded-1.p-1.px-2.card-highlight.word-wrap
			+bodyBgBadge(items[0])
			span.text-tiny.d-inline.d-sm-none #{utils.formatHex(hex, items[0])}
			span.small.d-none.d-sm-inline #{utils.formatHex(hex, items[0])}
			
			if (items[0] == "hex")
				.d-none.d-md-inline
					+copyTextButton(hex)

			div(id=`hex-display-expanded-${uniqueId}`, style="display: none;")
				each item, itemIndex in items
					if (itemIndex > 0)
						if (itemIndex > 1)
							br
						
						+bodyBgBadge(item)
						
						span.text-tiny.d-inline.d-sm-none #{utils.formatHex(hex, item)}
						span.small.d-none.d-sm-inline #{utils.formatHex(hex, item)}
						
						if (item == "hex")
							.d-inline.d-md-inline
								+copyTextButton(hex)
		

		div
			a.text-muted.ms-2(href="javascript:void(0)", onclick=`$('#hex-display-expanded-${uniqueId}').toggle(); $(this).find(".toggle-plus-minus").toggleClass("bi-plus-square"); $(this).find(".toggle-plus-minus").toggleClass("bi-dash-square"); return false;`, title="Toggle More Encodings")
				i.toggle-plus-minus.bi-plus-square.text-card-highlight

	


mixin timestamp(timestamp, options={includeAgo:true, agoOptions:{}})
	- let includeAgo = "includeAgo" in options ? options.includeAgo : true;
	- let agoOptions = "agoOptions" in options ? options.agoOptions : {};
	- let separateUtcDateIfNeeded = "separateUtcDateIfNeeded" in options ? options.separateUtcDateIfNeeded : false;
	
	- var tzDiff = -parseFloat(browserTzOffset);
	
	if (userTzOffset != "unset")
		- tzDiff = parseFloat(userTzOffset);

	- var tzMoment = moment.utc(new Date(parseInt(timestamp) * 1000)).add(tzDiff, "hours");
	- var utcMoment = moment.utc(new Date(parseInt(timestamp) * 1000));
	- var momentObj = (uiTimezone == "utc" ? utcMoment : tzMoment);
	- var nowMoment = (uiTimezone == "utc" ? moment.utc(new Date()) : moment.utc(new Date()).add(tzDiff, "hours"));

	- var yearStr = momentObj.format("Y");
	- var nowYearStr = nowMoment.format("Y");

	- var dateStr = momentObj.format("Y-M-D");
	- var nowDateStr = nowMoment.format("Y-M-D");


	- let differentDateUtc = false;
	if (tzDiff != 0 && uiTimezone == "local")
		- var titleStr = `${tzMoment.format("Y-MM-DD, h:mm:ss A")}<br/>(${utcMoment.format("Y-MM-DD, HH:mm:ss")} UTC)`;

		if (tzMoment.format("Y-MM-DD") != utcMoment.format("Y-MM-DD"))
			- differentDateUtc = true;

	else
		- var titleStr = `${utcMoment.format("Y-MM-DD, HH:mm:ss")} utc`;

	if (options.formatString)
		span.border-dotted(title=titleStr, data-bs-toggle="tooltip", data-bs-html="true")
			span.date-text(id=`xyz-${utils.getRandomString(10, "aA1")}`, data-timestamp-utc=`${parseInt(timestamp)}`, data-date-format=options.formatString, data-date-ago=includeAgo.toString()) #{momentObj.format(options.formatString)}
				if (["H", "m", "s"].some(str => options.formatString.includes(str)))
					span.text-tiny.text-tight-spacing.text-muted.ms-tiny UTC

	else if (dateStr == nowDateStr)
		span.border-dotted(title=titleStr, data-bs-toggle="tooltip", data-bs-html="true")
			if (tzDiff != 0 && uiTimezone == "local")
				span.date-text(id=`xyz-${utils.getRandomString(10, "aA1")}`, data-timestamp-utc=`${parseInt(timestamp)}`, data-date-ago=includeAgo.toString()) #{momentObj.format("h:mm")}
					span.text-tiny.text-tight-spacing.fw-light.ms-tiny #{momentObj.format("A")}

			else
				span #{momentObj.format("HH:mm")}
					span.text-tiny.text-tight-spacing.fw-light.ms-tiny UTC

	else if (yearStr == nowYearStr)
		span.border-dotted(title=titleStr, data-bs-toggle="tooltip", data-bs-html="true")
			if (tzDiff != 0 && uiTimezone == "local")
				span.date-text(id=`xyz-${utils.getRandomString(10, "aA1")}`, data-timestamp-utc=`${parseInt(timestamp)}`, data-date-ago=includeAgo.toString()) #{momentObj.format("M/D, h:mm")}
					span.text-tiny.text-tight-spacing.fw-light.ms-tiny #{momentObj.format("A")}

			else
				span #{momentObj.format("M/D, HH:mm")}
					span.text-tiny.text-tight-spacing.fw-light.ms-tiny UTC

	else
		span.border-dotted(title=titleStr, data-bs-toggle="tooltip", data-bs-html="true")
			if (tzDiff != 0 && uiTimezone == "local")
				span.date-text(id=`xyz-${utils.getRandomString(10, "aA1")}`, data-timestamp-utc=`${parseInt(timestamp)}`, data-date-ago=includeAgo.toString()) #{momentObj.format("YYYY-MM-DD")}
				
			else
				span #{momentObj.format("YYYY-MM-DD")}

	if (includeAgo)
		small.text-muted.ms-1 (
			+timeAgo(timestamp, agoOptions)
			| )

	if (differentDateUtc && separateUtcDateIfNeeded)
		div.text-muted
			span.ms-2 (
				span.text-tiny.text-tight-spacing.fw-light UTC:
			span.ms-1 #{utcMoment.format("M/D h:mm")}
			span.text-tiny.text-tight-spacing.fw-light.ms-tiny #{utcMoment.format("A")}
			| )




mixin timeAgo(timeAgoTime, options={oneElement:false, agoText:true, showPlus:true})
	- let showPlus = "showPlus" in options ? options.showPlus : true;

	- let oneElement = false;
	if (options && "oneElement" in options)
		- oneElement = options.oneElement;

	- let agoText = true;
	if (options && "agoText" in options)
		- agoText = options.agoText;

	- let hideHoverText = false;
	if (options && "hideHoverText" in options)
		- hideHoverText = options.hideHoverText;
	
	- let utcMoment = moment.utc(new Date(parseInt(timeAgoTime) * 1000));
	- let timeAgo = moment.duration(moment.utc(new Date()).diff(utcMoment));
	- let future = false;

	if (timeAgo.asSeconds() < 0)
		- future = true;
		- timeAgo = moment.duration(moment.utc(new Date(parseInt(timeAgoTime) * 1000)).diff(moment.utc(new Date())));


	if (future && showPlus)
		span +

		
	if (timeAgo.asHours() < 1)
		if (timeAgo.asMinutes() < 1)
			span #{timeAgo.seconds()}s
		else
			span #{timeAgo.minutes()}m

	else
		if (timeAgo.asHours() >= 1 && timeAgo.asHours() < 24)
			span #{timeAgo.hours()}h

			if (!oneElement && timeAgo.minutes() > 0)
				span.ms-1 #{timeAgo.minutes()}m

		else
			if (oneElement)
				if (hideHoverText)
					span #{utils.summarizeDuration(timeAgo, options)}
				else
					span.border-dotted(title=`${utils.summarizeDuration(timeAgo)}<br>${utcMoment.format("Y-MM-DD HH:mm:ss")} utc`, data-bs-toggle="tooltip", data-bs-html="true") #{utils.summarizeDuration(timeAgo, options)}
			else
				span #{utils.summarizeDuration(timeAgo, options)}

	if (!future && agoText)
		|  ago



mixin darkBadge(text)
	span.badge.text-bg-dark.border.border-dark.me-2 #{text ? text : ""}
		if (block)
			block


mixin lightBadge(text)
	span.badge.text-bg-light.text-dark.border.me-2 #{text ? text : ""}
		if (block)
			block


mixin successBadge(text)
	span.badge.text-bg-success.me-2 #{text ? text : ""}
		if (block)
			block


mixin primaryBadge(text, endMargin=true)
	span.badge.text-bg-primary(class=(endMargin ? "me-2" : false)) #{text ? text : ""}
		if (block)
			block

mixin secondaryBadge(text, endMargin=true)
	span.badge.text-bg-secondary(class=(endMargin ? "me-2" : false)) #{text ? text : ""}
		if (block)
			block

mixin infoBadge(text, endMargin=true)
	span.badge.text-bg-info(class=(endMargin ? "me-2" : false)) #{text ? text : ""}
		if (block)
			block


mixin funAlert(html, text, referenceUrl)
	.alert.alert-primary.shadow-sm.pb-0(role="alert", style="padding-left: 4rem;")
		i.bi-flag.fs-4.text-warning.d-inline-block(style="position: absolute; top: 0; left: 0; width: 40px; margin-left: 1.2rem; margin-top: 1.2rem;")

		h5.mb-2.fw-light #{coinConfig.name} Fun

		if (html)
			p
				span !{html}

				if (referenceUrl && referenceUrl.trim().length > 0 && html.indexOf(referenceUrl) == -1)
					span &nbsp;
						a(href=referenceUrl) Read more

		else if (text)
			p
				span #{text}

				if (referenceUrl && referenceUrl.trim().length > 0)
					span &nbsp;
						a(href=referenceUrl) Read more

		else if (block)
			p
				block


mixin funItemIcon
	i.bi-flag.text-warning



mixin blockCountdown(title, blocksLeft)
	- var countdownId = utils.getRandomString(10, "aA");
	.alert.alert-success
		.d-flex.justify-content-between
			div
				h5.mb-0
					span.me-2 #{title}:

					span.lead(id=`countdown-time-${countdownId}`)
						| #{blocksLeft.toLocaleString()} block#{blocksLeft > 1 ? "s" : ""}
						
						- var secondsToGo = 10 * 60 * blocksLeft;
						- var minutesToGo = secondsToGo / 60;
						- var hoursToGo = minutesToGo / 60;
						- var daysToGo = hoursToGo / 24;

						span.ms-1 (
						span ~
						if (daysToGo > 2)
							| #{parseInt(daysToGo)} days
							- var hours = 24 * (daysToGo - Math.floor(daysToGo));
							if (hours > 1)
								span.ms-1 #{parseInt(hours)} hour#{parseInt(hours) > 1 ? "s" :""}

						else if (daysToGo > 1)
							| 1 day
							- var hours = 24 * (daysToGo - Math.floor(daysToGo));
							if (hours > 1)
								span.ms-1 #{parseInt(hours)} hour#{parseInt(hours) > 1 ? "s" :""}

						else if (hoursToGo > 2)
							| #{parseInt(hoursToGo)} hours
							- var minutes = 60 * (hoursToGo - Math.floor(hoursToGo));
							if (minutes > 1)
								span.ms-1 #{parseInt(minutes)} minute#{parseInt(minutes) > 1 ? "s" :""}

						else if (hoursToGo >= 1)
							| 1 hour
							- var minutes = 60 * (hoursToGo - Math.floor(hoursToGo));
							if (minutes > 1)
								span.ms-1 #{parseInt(minutes)} minute#{parseInt(minutes) > 1 ? "s" :""}

						else
							- var minutes = 60 * (hoursToGo - Math.floor(hoursToGo));
							span #{parseInt(minutes)} minute#{parseInt(minutes) > 1 ? "s" :""}

						span )

			if (block)
				a.text-body(href="javascript:void(0);", onclick=`$("#countdown-block-${countdownId}").toggle(); $(".countdown-togglers-${countdownId}").toggle();`)
					i.bi-plus-square(class=`countdown-togglers-${countdownId}`)
					i.bi-dash-square(class=`countdown-togglers-${countdownId}`, style="display: none;")

		if (block)
			div(id=`countdown-block-${countdownId}`, style="display: none;")
				.mt-2
					block

		


mixin copyTextButton(text)
	small.ms-2
		if (false)
			a(href="javascript:void(0)", title="Copy", data-clipboard-text=text, data-bs-toggle="tooltip", onclick=`copyTextToClipboard("${text}"); $(".icon-copy").toggle(); $(this).find(".icon-copied").toggle(); setTimeout(() => { $(this).find(".icon-copy").toggle(); $(this).find(".icon-copied").toggle(); }, 2000); return false;`)
				i.bi-clipboard2.text-info.icon-copy
				i.bi-clipboard2-check.text-success.icon-copied(style="display: none;")

		a(href="javascript:void(0)", title="Copy", data-clipboard-text=text, data-bs-toggle="tooltip", onclick=`copyTextToClipboard("${text}"); $(this).attr("title", "Copied!").tooltip("_fixTitle").tooltip("show"); $(this).mouseleave(function() { $(this).tooltip("hide"); $(this).attr("data-bs-original-title", "Copy"); });`)
			i.bi-clipboard2.text-info


mixin dismissableInfoAlert(userSettingDismissedName, textToShowWhenClosed)
	- var hiddenByEnvVar = (config.site.hideInfoNotes == "true");
	if (!hiddenByEnvVar && (!userSettings.hideInfoNotes || userSettings.hideInfoNotes == "false"))
		if (userSettings[userSettingDismissedName] != "true")
			.alert.alert-primary.alert-dismissible.shadow-sm.mb-4.ps-3.ps-md-7(role="alert")
				i.d-none.d-md-inline.bi-info-circle.fs-5.d-inline-block(style="position: absolute; top: 0; left: 0; width: 36px; margin-left: 0.75rem; margin-top: 1.0rem;")

				block

				a.btn-close(href=`./changeSetting?name=${userSettingDismissedName}&value=true`, aria-label="Close", style="text-decoration: none;", title="Hide this note", data-bs-toggle="tooltip")
				//button.btn-close.text-muted(type="button", data-bs-dismiss="alert", aria-label="Close", title="Hide this note", data-bs-toggle="tooltip")

		else if (textToShowWhenClosed)
			.alert.alert-primary.shadow-sm.mb-4(role="alert")
				.clearfix
					.float-start
						i.bi-info-circle.me-3
						| #{textToShowWhenClosed}

					.float-end
						a.text-muted(href=`./changeSetting?name=${userSettingDismissedName}&value=false`, aria-label="Close", style="text-decoration: none;", title="Open this note", data-bs-toggle="tooltip")
							i.bi-plus-square


mixin dismissableAlert(userSettingDismissedName, textToShowWhenClosed)
	if (!userSettings.hideInfoNotes || userSettings.hideInfoNotes == "false")
		if (userSettings[userSettingDismissedName] != "true")
			.alert.alert-primary.alert-dismissible.shadow-sm.mb-4(role="alert")
				block

				a.btn-close(href=`./changeSetting?name=${userSettingDismissedName}&value=true`, aria-label="Close", style="text-decoration: none;", title="Hide this note", data-bs-toggle="tooltip")
				//button.btn-close.text-muted(type="button", data-bs-dismiss="alert", aria-label="Close", title="Hide this note", data-bs-toggle="tooltip")

		else if (textToShowWhenClosed)
			.alert.alert-primary.shadow-sm.mb-4(role="alert")
				.clearfix
					.float-start
						i.bi-info-circle.me-3
						| #{textToShowWhenClosed}

					.float-end
						a.text-muted(href=`./changeSetting?name=${userSettingDismissedName}&value=false`, aria-label="Close", style="text-decoration: none;", title="Open this note", data-bs-toggle="tooltip")
							i.bi-plus-square


mixin warningAlert
	.alert.alert-warning.shadow-sm.mb-4.ps-3.ps-md-7(role="alert")
		i.d-none.d-md-inline.bi-exclamation-triangle.fs-5.d-inline-block(style="position: absolute; top: 0; left: 0; width: 36px; margin-left: 0.7rem; margin-top: 1.0rem;")

		block


mixin numWithMutedDecimals(numString)
	if (numString.indexOf(".") >= 0)
		- var n = numString.substring(0, numString.indexOf("."));
		- var dec = numString.substring(numString.indexOf("."));

		span #{n.toLocaleString()}
		small.text-muted #{dec}

	else
		span #{parseInt(numString).toLocaleString()}
				

mixin blockRangeFilters
	.clearfix
		.float-start.me-4
			div(id="time-range-buttons")
				div.mb-2
					span.border-dotted.text-uppercase.fw-light.text-card-highlight(title="Analyze blocks over the last N days (approximated using average block time).", data-bs-toggle="tooltip") Time Range
				div.btn-group
					a.btn.btn-primary.block-count-btn(href="javascript:void(0)", data-blockCount="144") 1 day
					a.btn.btn-outline-primary.block-count-btn(href="javascript:void(0)", data-blockCount="432") 3 day
					a.btn.btn-outline-primary.block-count-btn(href="javascript:void(0)", data-blockCount="1008") 7 day
					a.btn.btn-outline-primary.block-count-btn(href="javascript:void(0)", data-blockCount="4320") 30 day

		.float-start.me-4
			div(id="block-selections-buttons")
				div.mb-2
					span.border-dotted.text-uppercase.fw-light.text-card-highlight(title="Choose from a list of pre-configured 'interesting' historical block ranges.", data-bs-toggle="tooltip") Interesting History

				div.dropdown
					button.btn.btn-outline-primary.dropdown-toggle(type="button", id="preconfigured-dropdown", data-bs-toggle="dropdown", aria-haspopup="true", aria-expanded="false") Selections
					div.dropdown-menu(aria-labelledby="preconfigured-dropdown")
						a.dropdown-item(href="javascript:void(0)", data-blocks="0-199") First 200 Blocks
						a.dropdown-item(href="javascript:void(0)", data-blocks="209900-210100") First Halving ±100
						a.dropdown-item(href="javascript:void(0)", data-blocks="419900-420100") Second Halving ±100
						a.dropdown-item(href="javascript:void(0)", data-blocks="629900-630100") Third Halving ±100
						a.dropdown-item(href="javascript:void(0)", data-blocks="481724-481924") SegWit Activation ±100

		.float-start
			div(id="time-range-buttons")
				div.mb-2
					span.border-dotted.text-uppercase.fw-light.text-card-highlight(title="Manually enter a range of blocks to analyze.", data-bs-toggle="tooltip") Custom Block Range

				form.form-inline.me-3(id="custom-range-form")
					div.input-group
						input.form-control(type="text", id="custom-range-start", placeholder="min height", style="width: 125px;")
						input.form-control(type="text", id="custom-range-end", placeholder="max height", style="width: 125px;")
						
						button.btn.btn-primary(type="submit") Go



mixin pagination(limit, offset, sort, itemCount, baseUrl, justification="start", large=false)
	- var pageNumber = offset / limit + 1;
	- var pageCount = Math.floor(itemCount / limit);
	- if (pageCount * limit < itemCount) {
		- pageCount++;
	- }
	- var paginationUrlFunction = function(x) {
		- return `${baseUrl}?limit=${limit}&offset=${(x - 1) * limit}${sort ? ("&sort=" + sort) : ""}`
		//- return baseUrl + "?limit=" + limit + "&offset=" + ((x - 1) * limit);
		//- return baseUrl + "?limit=" + limit + "&offset=" + ((x - 1) * limit + "&sort=" + sort);
	- }

	- var pageNumbers = [];
	- for (var x = 1; x <= pageCount; x++) {
		- pageNumbers.push(x);
	- }

	nav(aria-label="Page navigation")
		ul.pagination.flex-wrap.mb-0(class=`justify-content-${justification}`, class=(large ? "pagination-lg" : false))
			li.page-item(class=(pageNumber == 1 ? "disabled" : false))
				a.page-link(href=(pageNumber == 1 ? "javascript:void(0)" : paginationUrlFunction(pageNumber - 1)), aria-label="Previous")
					span(aria-hidden="true") &laquo;
			each x, xIndex in pageNumbers
				if (x >= (pageNumber - 4) && x <= (pageNumber + 4) || xIndex == 0 || xIndex == (pageNumbers.length - 1))
					li.page-item(class=(x == pageNumber ? "active" : false))
						a.page-link(href=(paginationUrlFunction(x))) #{x}

					if (x == 1 && pageNumber > 6)
						li.page-item.disabled
							a.page-link(href="javascript:void(0)") ...

				else if (x == (pageCount - 1) && pageNumber < (pageCount - 5))
					li.page-item.disabled
						a.page-link(href="javascript:void(0)") ...
				
			li.page-item(class=(pageNumber == pageCount ? "disabled" : false))
				a.page-link(href=(pageNumber == pageCount ? "javascript:void(0)" : paginationUrlFunction(pageNumber + 1)), aria-label="Next")
					span(aria-hidden="true") &raquo;


mixin graphPageScriptSetup(dateAdapterNeeded=false)
	script(src=assetUrl(`./js/chart.min.js`), integrity=assetIntegrity("chart.min.js"), crossorigin="anonymous")
	
	if (dateAdapterNeeded)
		script(src=assetUrl(`./js/moment.min.js`), integrity=assetIntegrity("moment.min.js"), crossorigin="anonymous")
		script(src=assetUrl(`./js/chartjs-adapter-moment.min.js`), integrity=assetIntegrity("chartjs-adapter-moment.min.js"), crossorigin="anonymous")
	
	if (userSettings.uiTheme && ["dark", "dark-v1"].includes(userSettings.uiTheme.toLowerCase()))
		script.
			Chart.defaults.color='white';
			var gridLineColor = "#1a4280";
	else
		script.
			Chart.defaults.color='black';
			var gridLineColor = "#dddddd";

	script(src=assetUrl('./js/decimal.js'))


mixin quote(quote, quoteIndex=-1, options={fontSize: 3, align:"start", includeQuotes:false})
	- let fontSize = (options && options.fontSize) ? options.fontSize : 3;
	- let align = (options && options.align) ? options.align : "start";
	- let includeQuotes = (options && options.includeQuotes != null) ? options.includeQuotes : false;

	blockquote.blockquote.mb-0(class=((align == "center") ? "text-center" : false ))
		span.font-serif.fw-light(class=`fs-${fontSize}`)
			
			if (options && options.includeQuotes)
				span(style="font-size: 110%;") "&nbsp;
			
			if (quoteIndex >= 0)
				a.text-reset(href=`./quote/${quoteIndex}`) #{quote.text}
			else
				span #{quote.text}
		
			if (options && options.includeQuotes)
				span(style="font-size: 110%;") &nbsp;"

		.ms-8.mt-2(class=`fs-${fontSize + 2}`)
			if (quote.speaker.indexOf("https://twitter.com") >= 0)
				- var account = quote.speaker.substring("https://twitter.com/".length);
				| — 
				a.fs-5(href=quote.speaker, target="_blank") @#{account}

			else
				span — #{quote.speaker}

			small.fw-light ,&nbsp;
				if (quote.url)
					a(href=quote.url, target="_blank") #{quote.date}

				else
					| #{quote.date}

			if (quote.context)
				div
					i.fs-6.text-muted.ms-2 (#{quote.context})
